using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;

namespace Microsoft.SqlServer.Management.SqlManagerUI.Scripting
{
using Server = Microsoft.SqlServer.Management.Smo.Server;
	internal class ScriptingEngine
	{
		private string _webServiceAddress;

		private string _webServiceUserName;

		private string _webServicePassword;

		private string _webServiceSqlServerName;

		private string _webServiceSqlDatabaseName;

		private string _webServiceSqlUserName;

		private string _webServiceSqlPassword;

		private bool _useTransactionForPublish = true;

		private ScriptingOptions scriptingOptions = new ScriptingOptions();

		private bool scriptCreate;

		private bool scriptDrop;

		private bool scriptData;

		private bool scriptDropData;

		private bool scriptBindings;

		private bool scriptPermission;

		private ScriptDestination scriptDestination;

		private string fileName = string.Empty;

		private string batchSeparator = string.Empty;

		private ScriptToFileMode fileMode;

        //private Encoding fileEncoding = Encoding.Unicode;
		private Encoding fileEncoding = Encoding.UTF8;

		private List<Urn> sourceObjects = new List<Urn>();

		private StringBuilder generatedScript;

		private ScriptSource scriptSource;

		private Server server;

        public event EventHandler<ScriptingProgressEventArgs> ScriptingProgress;
        //{
        //    [MethodImpl(MethodImplOptions.Synchronized)]
        //    add
        //    {
        //        this.ScriptingProgress += (EventHandler<ScriptingProgressEventArgs>)Delegate.Combine(this.ScriptingProgress, value);
        //    }
        //    [MethodImpl(MethodImplOptions.Synchronized)]
        //    remove
        //    {
        //        this.ScriptingProgress -= (EventHandler<ScriptingProgressEventArgs>)Delegate.Remove(this.ScriptingProgress, value);
        //    }
        //}

		public event EventHandler<ScriptingErrorEventArgs> ScriptingError;
        //{
        //    [MethodImpl(MethodImplOptions.Synchronized)]
        //    add
        //    {
        //        this.ScriptingError += (EventHandler<ScriptingErrorEventArgs>)Delegate.Combine(this.ScriptingError, value);
        //    }
        //    [MethodImpl(MethodImplOptions.Synchronized)]
        //    remove
        //    {
        //        this.ScriptingError -= (EventHandler<ScriptingErrorEventArgs>)Delegate.Remove(this.ScriptingError, value);
        //    }
        //}

        public event EventHandler<ObjectScriptedEventArgs> ObjectScripted;
        //{
        //    [MethodImpl(MethodImplOptions.Synchronized)]
        //    add
        //    {
        //        this.ObjectScripted += (EventHandler<ObjectScriptedEventArgs>)Delegate.Combine(this.ObjectScripted, value);
        //    }
        //    [MethodImpl(MethodImplOptions.Synchronized)]
        //    remove
        //    {
        //        this.ObjectScripted -= (EventHandler<ObjectScriptedEventArgs>)Delegate.Remove(this.ObjectScripted, value);
        //    }
        //}

		public bool ScriptCreate
		{
			get
			{
				return this.scriptCreate;
			}
			set
			{
				this.scriptCreate = value;
			}
		}

		public bool ScriptDrop
		{
			get
			{
				return this.scriptDrop;
			}
			set
			{
				this.scriptDrop = value;
			}
		}

		public bool ScriptData
		{
			get
			{
				return this.scriptData;
			}
			set
			{
				this.scriptData = value;
			}
		}

		public bool ScriptDropData
		{
			get
			{
				return this.scriptDropData;
			}
			set
			{
				this.scriptDropData = value;
			}
		}

		public string WebServiceAddress
		{
			get
			{
				return this._webServiceAddress;
			}
			set
			{
				this._webServiceAddress = value;
			}
		}

		public string WebServiceUserName
		{
			get
			{
				return this._webServiceUserName;
			}
			set
			{
				this._webServiceUserName = value;
			}
		}

		public string WebServicePassword
		{
			get
			{
				return this._webServicePassword;
			}
			set
			{
				this._webServicePassword = value;
			}
		}

		public string WebServiceSqlServerName
		{
			get
			{
				return this._webServiceSqlServerName;
			}
			set
			{
				this._webServiceSqlServerName = value;
			}
		}

		public string WebServiceSqlDatabaseName
		{
			get
			{
				return this._webServiceSqlDatabaseName;
			}
			set
			{
				this._webServiceSqlDatabaseName = value;
			}
		}

		public string WebServiceSqlUserName
		{
			get
			{
				return this._webServiceSqlUserName;
			}
			set
			{
				this._webServiceSqlUserName = value;
			}
		}

		public string WebServiceSqlPassword
		{
			get
			{
				return this._webServiceSqlPassword;
			}
			set
			{
				this._webServiceSqlPassword = value;
			}
		}

		public bool UseTransactionForPublish
		{
			get
			{
				return this._useTransactionForPublish;
			}
			set
			{
				this._useTransactionForPublish = value;
			}
		}

		public bool ScriptPermission
		{
			get
			{
				return this.scriptPermission;
			}
			set
			{
				this.scriptPermission = value;
			}
		}

		public bool ScriptHeader
		{
			get
			{
				return this.scriptingOptions.IncludeHeaders;
			}
		}

		public ScriptingOptions Options
		{
			get
			{
				return this.scriptingOptions;
			}
			set
			{
				this.scriptingOptions = value;
				this.scriptBindings = value.Bindings;
			}
		}

		public string FileName
		{
			get
			{
				return this.fileName;
			}
			set
			{
				this.fileName = value;
			}
		}

		public ScriptToFileMode ScriptToFileMode
		{
			get
			{
				return this.fileMode;
			}
			set
			{
				this.fileMode = value;
			}
		}

		public Encoding FileEncoding
		{
			get
			{
				return this.fileEncoding;
			}
			set
			{
				this.fileEncoding = value;
			}
		}

		public ScriptDestination Destination
		{
			get
			{
				return this.scriptDestination;
			}
			set
			{
				this.scriptDestination = value;
			}
		}

		public string BatchSeparator
		{
			get
			{
				return this.batchSeparator;
			}
			set
			{
				this.batchSeparator = value;
			}
		}

		public bool DatabaseContext
		{
			get
			{
				return this.scriptingOptions.IncludeDatabaseContext;
			}
		}

		public ScriptingEngine(Server server)
		{
			if (server == null)
			{
				throw new ArgumentNullException("server");
			}
			this.server = server;
		}

		public void Add(Urn urn)
		{
			if (urn == null)
			{
				throw new ArgumentNullException("urn");
			}
			if (!this.sourceObjects.Contains(urn))
			{
				this.sourceObjects.Add(urn);
			}
		}

		public void AddRange(IEnumerable<Urn> urns)
		{
			if (urns == null)
			{
				throw new ArgumentNullException("urns");
			}
			foreach (Urn current in urns)
			{
				this.Add(current);
			}
		}

		public void AddRange(UrnCollection urns)
		{
			if (urns == null)
			{
				throw new ArgumentNullException("urns");
			}
			foreach (Urn current in urns)
			{
				if (!(current.Type == "UnresolvedEntity"))
				{
					this.Add(current);
				}
			}
		}

		public IEnumerable<Urn> GetUrnsToScript()
		{
			this.scriptSource = ScriptSource.CreateScriptSource(this.server, this.sourceObjects.ToArray(), this.GetDiscoveryOptions(), !this.ScriptCreate && this.ScriptData);
			return this.scriptSource.ObjectsToScript;
		}

		public void GenerateScript()
		{
			ScriptProcessor scriptProcessor = this.GetScriptProcessor();
			scriptProcessor.ScriptingProgress += delegate(object sender, ScriptingProgressEventArgs e)
			{
				this.OnProgress(sender, e);
			};
			scriptProcessor.ScriptingError += delegate(object sender, ScriptingErrorEventArgs e)
			{
				if (this.ScriptingError != null)
				{
					this.ScriptingError(sender, e);
				}
			};
			scriptProcessor.ObjectScripted += delegate(object sender, ObjectScriptedEventArgs e)
			{
				if (this.ObjectScripted != null)
				{
					this.ObjectScripted(sender, e);
				}
			};
			this.SetupScriptTarget(scriptProcessor);
			this.OnProgress(this, new ScriptingProgressEventArgs(null, 0, 0, ScriptingEngineResources.ScriptingObjects));
			scriptProcessor.GenerateScript();
		}

		private void OnProgress(object sender, ScriptingProgressEventArgs e)
		{
			if (this.ScriptingProgress != null)
			{
				this.ScriptingProgress(sender, e);
			}
		}

		public string GetScript()
		{
			if (this.scriptDestination != ScriptDestination.SingleScript || this.generatedScript == null)
			{
				throw new InvalidOperationException();
			}
			return this.generatedScript.ToString();
		}

		private void SetupScriptTarget(ScriptProcessor scriptProcessor)
		{
			ScriptTarget scriptTarget = this.GetScriptTarget();
			scriptTarget.WritingObject += new EventHandler<ScriptingProgressEventArgs>(this.OnWritingObject);
			scriptProcessor.ObjectScripted += scriptTarget.ObjectScriptedHandler;
			scriptProcessor.ScriptingCompleted += new EventHandler(this.ScriptingCompleted);
			scriptProcessor.ScriptingCompleted += scriptTarget.ScriptingFinishedHandler;
		}

		private void OnWritingObject(object sender, ScriptingProgressEventArgs e)
		{
			string text = "";
			if (this.Destination == ScriptDestination.PublishToWeb)
			{
				if (e.ScriptAction == ScriptActions.Create)
				{
					text = ScriptingEngineResources.CreatingObject;
				}
				else if (e.ScriptAction == ScriptActions.Drop)
				{
					text = ScriptingEngineResources.DroppingObject;
				}
				else if (e.ScriptAction == ScriptActions.Data)
				{
					text = ScriptingEngineResources.InsertingObject;
				}
			}
			else if (this.Destination == ScriptDestination.SingleFile)
			{
				text = ScriptingEngineResources.WritingObject;
			}
			if (!string.IsNullOrEmpty(text))
			{
				this.OnProgress(sender, new ScriptingProgressEventArgs(ProgressEventType.WritingObject, string.Format(CultureInfo.CurrentCulture, text, new object[]
				{
					e.ObjectInfo.Urn.Type.ToLower(),
					e.ObjectInfo.FullName
				})));
			}
		}

		private void ScriptingCompleted(object sender, EventArgs e)
		{
			if (this.Destination == ScriptDestination.PublishToWeb)
			{
				this.OnProgress(sender, new ScriptingProgressEventArgs(null, 0, 0, ScriptingEngineResources.PublishingToWebService));
				return;
			}
			this.OnProgress(sender, new ScriptingProgressEventArgs(null, 0, 0, ScriptingEngineResources.WritingToFile));
		}

		private ScriptTarget GetScriptTarget()
		{
			switch (this.Destination)
			{
			case ScriptDestination.SingleScript:
				return this.GetSingleScriptTarget();
			case ScriptDestination.SingleFile:
				return this.GetSingleFileTarget();
			case ScriptDestination.FilePerObject:
				return this.GetFilePerObjectTarget();
			case ScriptDestination.PublishToWeb:
				return this.GetPublishToWebTarget();
			default:
				throw new InvalidOperationException();
			}
		}

		private Database TraverseUrnForDatabase(Urn urn)
		{
			object smoObject = this.server.GetSmoObject(urn.Value);
			if (smoObject == null)
			{
				return null;
			}
			if (smoObject.GetType() == typeof(Database))
			{
				return (Database)smoObject;
			}
			if (urn.Parent == null)
			{
				return null;
			}
			return this.TraverseUrnForDatabase(urn.Parent);
		}

		private ScriptTarget GetPublishToWebTarget()
		{
			this.OnProgress(this, new ScriptingProgressEventArgs(null, 0, 0, ScriptingEngineResources.ConnectingToWebService));
			Database database = null;
			if (this.scriptSource.ObjectsToScript.Count > 0)
			{
				database = this.TraverseUrnForDatabase(this.scriptSource.ObjectsToScript[0]);
			}
			if (this.UseTransactionForPublish)
			{
				foreach (Urn current in this.scriptSource.ObjectsToScript)
				{
					if (this.scriptingOptions.TargetServerVersion == SqlServerVersion.Version80 && (current.Type == "Role" || current.Type == "User"))
					{
						throw new InvalidOperationException(ScriptingEngineResources.ERROR_RoleOrUserOn2000WithTransaction);
					}
					if (current.Type == "FullTextCatalog")
					{
						throw new InvalidOperationException(ScriptingEngineResources.ERROR_FullTextCatalogWithTransaction);
					}
				}
			}
			PublishToWebWriterProvider publishToWebWriterProvider = new PublishToWebWriterProvider(this._webServiceAddress, this._webServiceUserName, this._webServicePassword, this._webServiceSqlServerName, this._webServiceSqlDatabaseName, this._webServiceSqlUserName, this._webServiceSqlPassword, this.BatchSeparator, this.UseTransactionForPublish);
			if (publishToWebWriterProvider.WebServiceOptions.HasDatabaseCollation && database != null && database.Collation != publishToWebWriterProvider.WebServiceOptions.DatabaseCollation)
			{
				this.OnProgress(this, new ScriptingProgressEventArgs(null, 0, 0, string.Format(CultureInfo.CurrentCulture, ScriptingEngineResources.ERROR_NonMatchingPublishDatabaseCollation, new object[]
				{
					database.Collation,
					publishToWebWriterProvider.WebServiceOptions.DatabaseCollation
				}), true));
			}
			return new SingleScriptTarget(this.scriptSource.CreateDependencies, this.scriptSource.DropDependencies, new ScriptWriterFactory(this.BatchSeparator, this.DatabaseContext, publishToWebWriterProvider));
		}

		private ScriptTarget GetFilePerObjectTarget()
		{
			return new FilePerObjectTarget(
                new ScriptWriterFactory(this.BatchSeparator, this.DatabaseContext, 
                new FilePerObjectWriterProvider(this.FileName, this.ScriptToFileMode, this.FileEncoding)));
		}

		private ScriptTarget GetSingleFileTarget()
		{
			return new SingleScriptTarget(this.scriptSource.CreateDependencies, this.scriptSource.DropDependencies, new ScriptWriterFactory(this.BatchSeparator, this.DatabaseContext, new SingleFileWriterProvider(this.FileName, this.ScriptToFileMode, this.FileEncoding)));
		}

		private ScriptTarget GetSingleScriptTarget()
		{
			this.generatedScript = new StringBuilder();
			return new SingleScriptTarget(this.scriptSource.CreateDependencies, this.scriptSource.DropDependencies, new ScriptWriterFactory(this.BatchSeparator, this.DatabaseContext, new SingleScriptWriterProvider(this.generatedScript)));
		}

		private ScriptProcessor GetScriptProcessor()
		{
			return new ScriptProcessor(this.scriptSource.ObjectsToScript, this.GetFilteredScriptingOptions(), this.GetScriptCommandFactory());
		}

		private ScriptObjectCommandFactory GetScriptCommandFactory()
		{
			return new ScriptObjectCommandFactory(this.server, this.GetScriptAction(), this.GetTableChildObjects());
		}

		private DiscoveryTasks GetDiscoveryOptions()
		{
			DiscoveryTasks discoveryTasks = DiscoveryTasks.None;
			if (this.Options.WithDependencies)
			{
				if (this.ScriptCreate)
				{
					discoveryTasks |= DiscoveryTasks.CreateDependencies;
				}
				if (this.ScriptDrop)
				{
					discoveryTasks |= DiscoveryTasks.DropDependencies;
				}
			}
			if (this.Options.Triggers)
			{
				discoveryTasks |= DiscoveryTasks.Triggers;
			}
			return discoveryTasks;
		}

		private ScriptActions GetScriptAction()
		{
			ScriptActions scriptActions = ScriptActions.None;
			if (this.ScriptCreate)
			{
				scriptActions |= ScriptActions.Create;
			}
			if (this.ScriptDrop)
			{
				scriptActions |= ScriptActions.Drop;
			}
			if (this.ScriptPermission)
			{
				scriptActions |= ScriptActions.Permission;
			}
			if (this.ScriptHeader)
			{
				scriptActions |= ScriptActions.Header;
			}
			if (this.ScriptData)
			{
				scriptActions |= ScriptActions.Data;
			}
			if (this.ScriptDropData)
			{
				scriptActions |= ScriptActions.DropData;
			}
			if (this.scriptBindings)
			{
				scriptActions |= ScriptActions.Bindings;
			}
			return scriptActions;
		}

		private TableChildObjects GetTableChildObjects()
		{
			TableChildObjects tableChildObjects = TableChildObjects.None;
			if (this.Options.DriChecks)
			{
				tableChildObjects |= TableChildObjects.Check;
			}
			if (this.Options.DriForeignKeys)
			{
				tableChildObjects |= TableChildObjects.ForeignKey;
			}
			if (this.Options.FullTextIndexes)
			{
				tableChildObjects |= TableChildObjects.FullTextIndex;
			}
			if (this.Options.DriDefaults)
			{
				tableChildObjects |= TableChildObjects.DriDefaults;
			}
			if (this.ScriptData)
			{
				tableChildObjects |= TableChildObjects.Data;
			}
			if (this.Options.Bindings)
			{
				tableChildObjects |= TableChildObjects.Bindings;
			}
			return tableChildObjects;
		}

		private ScriptingOptions GetFilteredScriptingOptions()
		{
			return new ScriptingOptions(this.Options)
			{
				Bindings = false,
				Permissions = false,
				FullTextIndexes = false,
				Triggers = false,
				WithDependencies = false,
				IncludeHeaders = false,
				IncludeDatabaseContext = false,
				DriDefaults = false
			};
		}
	}
}
